﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Micro.Extensions.Cache.Redis.Extensions;

namespace Micro.Extensions.Cache.Redis
{
    internal class RedisDatabaseProvider : IRedisDatabaseProvider
    {
        private readonly ILogger _logger;
        /// <summary>
        /// The connection multiplexer.
        /// </summary>
        private readonly IConnectionMultiplexer _connectionMultiplexer;



        public RedisDatabaseProvider(IConnectionMultiplexer connectionMultiplexer, ILogger<RedisDatabaseProvider> logger)
        {
            _logger = logger;
            _connectionMultiplexer = connectionMultiplexer;
            _connectionMultiplexer.ConnectionFailed += (s, e) => Log("Redis ConnectionFailed：" + JsonConvert.SerializeObject(e), e.Exception);
            _connectionMultiplexer.ConnectionRestored += (s, e) => Log("Redis ConnectionRestored：" + JsonConvert.SerializeObject(e), e.Exception);
            _connectionMultiplexer.ErrorMessage += (s, e) => Log("Redis ErrorMessage：" + JsonConvert.SerializeObject(e), null);
            _connectionMultiplexer.InternalError += (s, e) => Log("Redis InternalError：" + JsonConvert.SerializeObject(e), e.Exception);
            _connectionMultiplexer.ConfigurationChanged += (s, e) => Log("Redis ConfigurationChanged：" + JsonConvert.SerializeObject(e), null);
            _connectionMultiplexer.ConfigurationChangedBroadcast += (s, e) => Log("Redis ConfigurationChangedBroadcast：" + JsonConvert.SerializeObject(e), null);
            _connectionMultiplexer.HashSlotMoved += (s, e) => Log("Redis HashSlotMoved" + JsonConvert.SerializeObject(e), null);
        }

        private void Log(string message, Exception e)
        {
            _logger.LogRedis(LogLevel.Error, message, e);
        }

        /// <summary>
        /// Gets the database connection.
        /// </summary>
        public IDatabase GetDatabase()
        {
            return _connectionMultiplexer.GetDatabase();
        }

        /// <summary>
        /// Gets the server list.
        /// </summary>
        /// <returns>The server list.</returns>
        public IEnumerable<IServer> GetServerList()
        {
            var endpoints = GetMastersServersEndpoints();

            foreach (var endpoint in endpoints)
            {
                yield return _connectionMultiplexer.GetServer(endpoint);
            }
        }

        /// <summary>
        /// Gets the masters servers endpoints.
        /// </summary>
        private List<EndPoint> GetMastersServersEndpoints()
        {
            var masters = new List<EndPoint>();
            foreach (var ep in _connectionMultiplexer.GetEndPoints())
            {
                var server = _connectionMultiplexer.GetServer(ep);
                if (server.IsConnected)
                {
                    //Cluster
                    if (server.ServerType == ServerType.Cluster)
                    {
                        masters.AddRange(server.ClusterConfiguration.Nodes.Where(n => !n.IsReplica).Select(n => n.EndPoint));
                        break;
                    }
                    // Single , Master-Slave
                    if (server.ServerType == ServerType.Standalone && !server.IsReplica)
                    {
                        masters.Add(ep);
                        break;
                    }
                }
            }
            return masters;
        }

        public void Dispose()
        {
            _connectionMultiplexer?.Dispose();
        }
    }
}
